﻿#include "unit_test.h"
#include <iostream>

using namespace DirectX;
using namespace std;

#include "../window/window_app.h"

namespace ifire {

static const char* ENTER = "\r\n";
static const char* READY = "READY";
static TestConsole* g_console;

#define ID_BUTTON_START_TEST 1001
#define ID_BUTTON_CLEAR_CONSOLE 1002
#define ID_BUTTON_PRINT_LOG 1003
#define ID_BUTTON_CHECK_COMPILE_SHADER 1004
#define ID_BUTTON_OPEN_ROOM 1005
#define ID_BUTTON_OPEN_MOUNTAIN_WATER 1006
#define ID_BUTTON_OPEN_VEC_ADD 1007

#define ID_BUTTON_OPEN_START 1100
#define ID_BUTTON_OPEN_END 1199

TestConsole::TestConsole(WindowApp* parent, int editHeight)
    : parent_(parent), editHeight(editHeight), fontSelected(false) {
  g_console = this;
}

TestConsole::~TestConsole() {}

void TestConsole::Init() {

  RECT rect = {0, GetHeight() - editHeight, GetWidth() - 20, editHeight - 70};
  hEdit = parent_->CreateTextArea(rect);

  BuildActions();

  AddButton("开始测试", ID_BUTTON_START_TEST);
  AddButton("打印日志", ID_BUTTON_PRINT_LOG);
  AddButton("清空记录", ID_BUTTON_CLEAR_CONSOLE);

  hLabels[0] = parent_->CreateLabel("READY", {5, 5, GetWidth() - 30, 25});
  hLabels[1] = parent_->CreateLabel("READY", {5, 35, GetWidth() - 30, 25});
  hLabels[2] = parent_->CreateLabel("READY", {5, 65, GetWidth() - 30, 25});

  SetDebug(0, "调试控制台已经准备就绪.");
}

void TestConsole::BuildActions() {
  auto app = WindowApp::GetApp();
  auto actions = app->GetActions();
  int index_id = 0;
  for (auto it = actions.begin(); it != actions.end(); ++it, ++index_id) {
    auto& name = it->first;
    AddActionButton(name.c_str(), index_id);
    // it->first 是键，it->second 是值
    //std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;
  }
}

void TestConsole::Print(const char* format, ...) {
  // 将参数打印为整条字符串
  va_list ap;
  va_start(ap, format);
  vsprintf_s(this->printBuffer, format, ap);
  va_end(ap);

  AddText(printBuffer, false);
}

void TestConsole::PrintLine(const char* format, ...) {
  // 将参数打印为整条字符串
  va_list ap;
  va_start(ap, format);
  vsprintf_s(this->printBuffer, format, ap);
  va_end(ap);

  AddText(printBuffer, true);
}

void TestConsole::FlushLog() {
  auto log = ikit::get_log();
  AddText(log.c_str(), true);
}

void TestConsole::SetDebug(int index, const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  vsprintf_s(this->printBuffer, format, ap);
  va_end(ap);

  SetWindowText(hLabels[index], printBuffer);
}

void TestConsole::SetText(const char* str) { SetWindowText(hEdit, str); }

void TestConsole::OnPaint(HDC hdc) {
  if (fontSelected == false) {
    SelectObject(hdc, parent_->GetFont());
    fontSelected = true;
  }
  TextOut(hdc, 0, 0, READY, static_cast<int>(strlen(READY)));
}

void TestConsole::OnCommand(int id) {
  if (id >= ID_BUTTON_OPEN_START && id <= ID_BUTTON_OPEN_END){
    WindowApp::GetApp()->ExecuteAction(id - ID_BUTTON_OPEN_START);
    return;
  }

  if (ID_BUTTON_START_TEST == id) {
    OnStartTest();
  } else if (ID_BUTTON_CLEAR_CONSOLE == id) {
    SetText("");
  } else if (ID_BUTTON_PRINT_LOG == id) {
    SetText("");
    FlushLog();
  } 
}

HWND TestConsole::AddButton(const char* button_name, int button_id) {
  RECT rect = {button_align_pos_, GetButtonTop(), 100, 30};
  auto handle = parent_->AddButton(button_name, button_id, rect);
  button_align_pos_ += 110;
  return handle;
}

HWND TestConsole::AddActionButton(const char* button_name, int index_id) {

  RECT rect = {action_align_pos_, GetActionTop() + action_top_pos_, 100, 30};
  auto handle = parent_->AddButton(button_name, ID_BUTTON_OPEN_START + index_id, rect);
  action_align_pos_ += 110;  
  if (index_id == 10){
    action_top_pos_ += 35;
    action_align_pos_ = 5;
  }

  return handle;
}


void TestConsole::AddText(const char* str, bool newLine) const {
  parent_->AddTextToEdit(hEdit, str, newLine);
}

void TestConsole::SignTest(const char* str, LPUnitTest unitTest) {
  std::tuple<const char*, LPUnitTest> myTuple(str, unitTest);
  testList.push_back(myTuple);
}

void TestConsole::OnStartTest() {
  for (std::tuple<const char*, LPUnitTest> item : testList) {
    SetDebug(0, "Start %s...", std::get<0>(item));
    std::get<1>(item)->RunTest();
  }
}

UnitTest::UnitTest() : console(g_console), startTime(0.0f), endTime(0.0f) {}

void XM_CALLCONV UnitTest::PrintVector(
    const char* name, DirectX::FXMVECTOR value) {
  XMFLOAT3 dest;
  XMStoreFloat3(&dest, value);
  console->PrintLine("%s = (%.3f, %.3f, %.3f)", name, dest.x, dest.y, dest.z);
}

void XM_CALLCONV UnitTest::PrintVector4(
    const char* name, DirectX::FXMVECTOR value) {
  XMFLOAT4 dest;
  XMStoreFloat4(&dest, value);
  console->PrintLine(
      "%s = (%.3f, %.3f, %.3f, %.3f)", name, dest.x, dest.y, dest.z, dest.w);
}

void XM_CALLCONV UnitTest::PrintMatrix(
    const char* name, DirectX::FXMMATRIX value) {
  XMFLOAT4X4 mat;
  XMStoreFloat4x4(&mat, value);
  console->PrintLine("%s = ", name);
  console->PrintLine(
      "[%.3f, %.3f, %.3f, %.3f]", mat._11, mat._12, mat._13, mat._14);
  console->PrintLine(
      "[%.3f, %.3f, %.3f, %.3f]", mat._21, mat._22, mat._23, mat._24);
  console->PrintLine(
      "[%.3f, %.3f, %.3f, %.3f]", mat._31, mat._32, mat._33, mat._34);
  console->PrintLine(
      "[%.3f, %.3f, %.3f, %.3f]", mat._41, mat._42, mat._43, mat._44);
}

void UnitTest::PrintFloat(const char* name, float value) {
  console->PrintLine("%s = %.3f", name, value);
}

void UnitTest::Assert(const char* description, HRESULT result) {
  if (result == S_OK) {
    PrintLine("%s (SUCCESS)", description);
  } else {
    PrintLine("%s (FAILED)", description);
  }
}

void UnitTest::Assert(const char* description, bool result) {
  if (result) {
    PrintLine("%s (SUCCESS)", description);
  } else {
    PrintLine("%s (FAILED)", description);
  }
}

void UnitTest::Print(const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  vsprintf_s(this->printBuffer, format, ap);
  va_end(ap);

  console->AddText(printBuffer, false);
}

void UnitTest::PrintLine(const char* format, ...) {
  va_list ap;
  va_start(ap, format);
  vsprintf_s(this->printBuffer, format, ap);
  va_end(ap);

  console->AddText(printBuffer, true);
}

void UnitTest::FlushLog() { console->FlushLog(); }

std::string wchar_to_char(const std::wstring& wideStr) {
  // 计算需要的缓冲区大小
  int multiByteLen = WideCharToMultiByte(
      CP_ACP, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
  if (multiByteLen == 0) {
    // 处理错误
    return std::string();
  }

  // 分配缓冲区并进行转换
  std::string multiByteStr(multiByteLen, 0);
  WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &multiByteStr[0],
      multiByteLen, nullptr, nullptr);

  return multiByteStr;
}

} // namespace ifire